package org.qcit.plugins

import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.Format
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.TransformOutputProvider
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.qcit.timeplugin.TimeCountClassVisitor

import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry

/**
 * class jar 过滤
 */
class FileHandle {
    /**
     * 处理文件目录下的class文件
     */
    static handleDirectoryInput(DirectoryInput directoryInput, TransformOutputProvider outputProvider,
                                List<String> includePages,long threshold) {
        //是否是目录
        if (directoryInput.file.isDirectory()) {
            //列出目录中所有的文件（包含文件夹，子文件夹内的文件）
            directoryInput.file.eachFileRecurse { file ->
                def name = file.name
                def result = filterClass(name, includePages)
                println("================result======="+result)
                //过滤文件
                if (result) {
                    println("================name:"+name)
                    def classReader = new ClassReader(file.bytes)
                    def classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES)
                    def classVisitor = new TimeCountClassVisitor(classWriter,threshold)
                    classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
                    def code = classWriter.toByteArray()
                    def fos = new FileOutputStream(file.parentFile.absolutePath + File.separator + name)
                    fos.write(code)
                    fos.close()
                }
            }
        }
        //文件夹里面包含的是我们手写的类以及R.class、BuildConfig.class以及R$XXX.class等
        // 获取output目录
        def dest = outputProvider.getContentLocation(
                directoryInput.name,
                directoryInput.contentTypes,
                directoryInput.scopes,
                Format.DIRECTORY)
        //执行字节码注入，不操作字节码的话也要将输入路径拷贝到输出路径进行文件合并
        FileUtils.copyDirectory(directoryInput.file, dest)
    }
    /**
     * 处理文jar中的class文件 组件化开发model以jar的形式依赖
     */
    static handleJarInput(JarInput jarInput, TransformOutputProvider outputProvider,
                          List<String> includePages,long threshold) {
        if (jarInput.file.getAbsolutePath().endsWith(".jar")) {
            //重命名输出文件，防止文件覆盖
            def jarName = jarInput.name
            def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
            if (jarName.endsWith(".jar")) {
                jarName = jarName.substring(0, jarName.length() - 4)
            }
            def jarFile = new JarFile(jarInput.file)
            def enumeration = jarFile.entries()
            def tmpFile = new File(jarInput.file.getParent() + File.separator + "temp.jar")
            //避免上次的缓存被重新插入
            if (tmpFile.exists()) {
                tmpFile.delete()
            }
            def jarOutputStream = new JarOutputStream(new FileOutputStream(tmpFile))
            //保存
            while (enumeration.hasMoreElements()) {
                def jarEntry = enumeration.nextElement()
                def entryName = jarEntry.getName()
                def zipEntry = new ZipEntry(entryName)
                def inputStream = jarFile.getInputStream(jarEntry)
                //class 插桩
                if (filterClass(entryName, includePages)) {
                    jarOutputStream.putNextEntry(zipEntry)
                    def classReader = new ClassReader(IOUtils.toByteArray(inputStream))
                    def classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
                    def classVisitor = new TimeCountClassVisitor(classWriter,threshold)
                    classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
                    def code = classWriter.toByteArray()
                    jarOutputStream.write(code)
                } else {
                    jarOutputStream.putNextEntry(zipEntry)
                    jarOutputStream.write(IOUtils.toByteArray(inputStream))
                }
                jarOutputStream.closeEntry()
            }
            jarOutputStream.close()
            jarFile.close()
            //生成输出路径 + md5Name
            def dest = outputProvider.getContentLocation(
                    jarName + md5Name,
                    jarInput.contentTypes,
                    jarInput.scopes,
                    Format.JAR
            )
            FileUtils.copyFile(tmpFile, dest)
            tmpFile.delete()
        }
    }

    /**
     * 检查class文件是否需要处理
     * @param fileName
     * @return
     */
    static boolean filterClass(String name, List<String> includePages) {
        //格式为*****/***/***/**.classs
        println("================name======="+name)
        if (!name.endsWith(".class")) return false
        if (name.indexOf("/")> -1){
            def localName = name.substring(0, name.lastIndexOf("/"))
            def suffix = name.substring(name.lastIndexOf("/") + 1 ,name.length())
            localName = localName.replaceAll("/", ".")
            for (int i = 0;i< includePages.size();i++){
                if (includePages.get(i).startsWith(localName)){

                    return  !suffix.startsWith("R\$")
                            && !name.startsWith("androidX")
                            && !name.startsWith("android")
                            && "R.class" != suffix
                            && "BuildConfig.class" != suffix
                }
            }

        }

        println("================走着里了======="+name)
        println("================!name.startsWith(\"R\\\$\")======="+!name.startsWith("R\$"))
        println("================!name.startsWith(\"androidX\")======="+!name.startsWith("androidX"))
        println("================!name.startsWith(\"android\")======="+!name.startsWith("android"))
        println("================R.class != name======="+("R.class" != name))
        println("================BuildConfig.class!= name======="+("BuildConfig.class" != name))
        //格式为 **.classs
        boolean res =    (!name.startsWith("R\$"))
                && (!name.startsWith("androidX"))
                && (!name.startsWith("android"))
                && ("R.class" != name)
                && ("BuildConfig.class" != name);
        System.out.println("======return========"+res);
        return res;

    }
}